﻿// <copyright file="Vector3D.cs" company="Axelerate Solutions S.A.">
// Copyright (c) Axelerate Solutions S.A.  All rights reserved.
// </copyright>

namespace Axelerate.Silverlight3D.Media.Media3D
{
    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.ComponentModel;
        
    /// <summary>
    /// File name: Vector3D.cs
    /// Company: Axelerate Solutions S.A.
    /// Created By: Daniel Rojas
    /// Created Date: 01/30/2008 16:12:00 h.
    /// Description: Represents a displacement in 3-D space.
    ///   <Modifications>
    ///   </Modifications>
    /// </summary>
    [TypeConverterAttribute(typeof(Vector3DConverter))]
    public struct Vector3D : IFormattable
    {
        #region Fields
        /// <summary>
        ///  The x-coordinate of this Point3D.
        /// </summary>
        private double mX;

        /// <summary>
        ///  The y-coordinate of this Point3D.
        /// </summary>
        private double mY;

        /// <summary>
        ///  The z-coordinate of this Point3D.
        /// </summary>
        private double mZ;
        #endregion

        #region Properties
        /// <summary>
        ///  The x-coordinate of this Point3D.
        /// </summary>
        public double X
        {
            get
            {
                return mX;
            }
            set
            {
                mX = value;
            }
        }

        /// <summary>
        ///  The y-coordinate of this Point3D.
        /// </summary>
        public double Y
        {
            get
            {
                return mY;
            }
            set
            {
                mY = value;
            }
        }

        /// <summary>
        ///  The z-coordinate of this Point3D.
        /// </summary>
        public double Z
        {
            get
            {
                return mZ;
            }
            set
            {
                mZ = value;
            }
        }

        /// <summary>
        /// Gets the length of this Vector3D structure.
        /// </summary>
        public double Length 
        {
            get
            {
                return Math.Sqrt(this.LengthSquared); 
            }
        }

        /// <summary>
        /// Gets the square of the length of this Vector3D structure.
        /// </summary>
        public double LengthSquared 
        {
            get
            {
                return mX * mX + mY * mY + mZ * mZ; 
            }
        }

        /// <summary>
        /// Divides the specified Vector3D structure by the specified scalar and returns the result as a Vector3D.
        /// </summary>
        /// <param name="vector">The Vector3D structure to be divided.</param>
        /// <param name="scalar">The scalar to divide vector by.</param>
        /// <returns>Returns the result of dividing vector by scalar.</returns>
        public static Vector3D operator /(Vector3D vector, double scalar)
        {
            return Vector3D.Divide(vector, scalar);
        }

        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the Vector3D structure.
        /// </summary>
        /// <param name="x"><typeparamref name="System.Double"/>The X value of the new Vector3D structure.</param>
        /// <param name="y"><typeparamref name="System.Double"/>The Y value of the new Vector3D structure.</param>
        /// <param name="z"><typeparamref name="System.Double"/>The Z value of the new Vector3D structure.</param>
        public Vector3D(double x, double y, double z)
        {
            mX = x;
            mY = y;
            mZ = z;
        }
        #endregion

        #region Operators
        /// <summary>
        /// Translates the specified Point3D structure by the specified Vector3D structure and returns the result as a Point3D structure.
        /// </summary>
        /// <param name="vector">The Vector3D structure used to translate the specified Point3D structure.</param>
        /// <param name="point">The Point3D structure to be translated.</param>
        /// <returns>Returns the result of translating point by vector.</returns>
        public static Point3D operator +(Vector3D vector, Point3D point)
        {
            return Vector3D.Add(vector, point);
        }

        /// <summary>
        /// Adds two Vector3D structures and returns the result as a Vector3D structure.
        /// </summary>
        /// <param name="vector1">The first Vector3D structure to add.</param>
        /// <param name="vector2">The second Vector3D structure to add.</param>
        /// <returns>Returns the sum of vector1 and vector2.</returns>
        public static Vector3D operator +(Vector3D vector1, Vector3D vector2)
        {
            return Vector3D.Add(vector1, vector2);
        }

        /// <summary>
        /// Compares two Vector3D structures for equality.
        /// </summary>
        /// <param name="vector1">The first Vector3D structure to compare.</param>
        /// <param name="vector2">The second Vector3D structure to compare.</param>
        /// <returns>True if the X, Y, and Z components of vector3D1 and vector3D2 are equal; false otherwise.</returns>
        public static bool operator ==(Vector3D vector1, Vector3D vector2)
        {
            return Vector3D.Equals(vector1, vector2);
        }

        /// <summary>
        /// Converts a Vector3D structure into a Point3D structure.
        /// </summary>
        /// <param name="vector">The vector to convert.</param>
        /// <returns>The result of converting vector.</returns>
        public static explicit operator Point3D(Vector3D vector)
        {
            return new Point3D(vector.X, vector.Y, vector.Z);
        }

        /// <summary>
        /// Compares two Vector3D structures for inequality.
        /// </summary>
        /// <param name="vector1">The first Vector3D structure to compare.</param>
        /// <param name="vector2">The second Vector3D structure to compare.</param>
        /// <returns>True if the X, Y, and Z components of vector3D1 and vector3D2 are different; false otherwise.</returns>
        public static bool operator !=(Vector3D vector1, Vector3D vector2)
        {
            return !Vector3D.Equals(vector1, vector2);
        }

        /// <summary>
        /// Multiplies the specified scalar by the specified Vector3D structure and returns the result as a Vector3D.
        /// </summary>
        /// <param name="scalar">The scalar to multiply.</param>
        /// <param name="vector">The Vector3D structure to multiply.</param>
        /// <returns>Returns the result of multiplying scalar and vector.</returns>
        public static Vector3D operator *(double scalar, Vector3D vector)
        {
            return Vector3D.Multiply(scalar, vector);
        }

        /// <summary>
        /// Multiplies the specified scalar by the specified Vector3D structure and returns the result as a Vector3D.
        /// </summary>
        /// <param name="vector">The Vector3D structure to multiply.</param>
        /// <param name="scalar">The scalar to multiply.</param>
        /// <returns>Returns the result of multiplying scalar and vector.</returns>
        public static Vector3D operator *(Vector3D vector, double scalar)
        {
            return Vector3D.Multiply(vector, scalar);
        }

        /// <summary>
        /// Subtracts a Point3D structure from a Vector3D structure.
        /// </summary>
        /// <param name="vector">The Vector3D structure to be subtracted from.</param>
        /// <param name="point">The Point3D structure to subtract from vector.</param>
        /// <returns>Returns the result of subtracting point from vector.</returns>
        public static Point3D operator -(Vector3D vector, Point3D point)
        {
            return Vector3D.Subtract(vector, point);
        }

        /// <summary>
        /// Subtracts a Vector3D structure from a Vector3D structure.
        /// </summary>
        /// <param name="vector1">The Vector3D structure to be subtracted from.</param>
        /// <param name="vector2">The Vector3D structure to subtract from vector1.</param>
        /// <returns>Returns the result of subtracting vector2 from vector1.</returns>
        public static Vector3D operator -(Vector3D vector1, Vector3D vector2)
        {
            return Vector3D.Subtract(vector1, vector2);
        }

        /// <summary>
        /// Negates a Vector3D structure.
        /// </summary>
        /// <param name="vector">The Vector3D structure to negate.</param>
        /// <returns>Returns a Vector3D structure with X, Y, and Z values opposite of the X, Y, and Z values of vector.</returns>
        public static Vector3D operator -(Vector3D vector)
        {
            return new Vector3D(vector.X * -1, vector.Y * -1, vector.Z * -1);
        }

        #endregion

        #region Methods
        /// <summary>
        /// Translates the specified Point3D structure by the specified Vector3D structure and returns the result as a Point3D structure.
        /// </summary>
        /// <param name="vector">The Vector3D structure used to translate the specified Point3D structure.</param>
        /// <param name="point">The Point3D structure to be translated.</param>
        /// <returns>Returns the result of translating point by vector.</returns>
        public static Point3D Add(Vector3D vector, Point3D point)
        {
            return Point3D.Add(point, vector);
        }

        /// <summary>
        /// Adds two Vector3D structures and returns the result as a Vector3D structure.
        /// </summary>
        /// <param name="vector1">The first Vector3D structure to add.</param>
        /// <param name="vector2">The second Vector3D structure to add.</param>
        /// <returns>Returns the sum of vector1 and vector2.</returns>
        public static Vector3D Add(Vector3D vector1, Vector3D vector2)
        {
            return new Vector3D(vector1.mX + vector2.mX, vector1.mY + vector2.mY, vector1.mZ + vector2.mZ);  
        }

        /// <summary>
        /// Calculates the dot product of two Vector3D structures.
        /// </summary>
        /// <param name="vector1">The first Vector3D structure to evaluate.</param>
        /// <param name="vector2">The second Vector3D structure to evaluate.</param>
        /// <returns>Returns the dot product of vector1 and vector2.</returns>
        public static double DotProduct(Vector3D vector1, Vector3D vector2)
        {
            return vector1.mX * vector2.mX + vector1.mY * vector2.mY + vector1.mZ * vector2.mZ;
        }

        /// <summary>
        /// Retrieves the angle required to rotate the first specified Vector3D structure into the second specified Vector3D structure.
        /// </summary>
        /// <param name="vector1">The first Vector3D structure to evaluate.</param>
        /// <param name="vector2">The second Vector3D structure to evaluate.</param>
        /// <returns>Returns the angle in degrees needed to rotate vector1 into vector2.</returns>
        public static double AngleBetween(Vector3D vector1, Vector3D vector2)
        {
            double lengthV1 = vector1.Length;
            double lengthV2 = vector2.Length;
            if (lengthV1 != 0 && lengthV2 != 0)
            {
                return (180 / Math.PI) * Math.Acos(Vector3D.DotProduct(vector1, vector2) / (vector1.Length * vector2.Length));
            }
            return 0;
        }

        /// <summary>
        /// Calculates the cross product of two Vector3D structures.
        /// </summary>
        /// <param name="vector1">The first Vector3D structure to evaluate.</param>
        /// <param name="vector2">The second Vector3D structure to evaluate.</param>
        /// <returns>Returns the cross product of vector1 and vector2.</returns>
        public static Vector3D CrossProduct(Vector3D vector1, Vector3D vector2)
        {
            return new Vector3D((vector1.Y * vector2.Z - vector1.Z * vector2.Y),
               (vector1.Z * vector2.X - vector1.X * vector2.Z),
               (vector1.X * vector2.Y - vector1.Y * vector2.X));
        }

        /// <summary>
        /// Divides the specified Vector3D structure by the specified scalar and returns the result as a Vector3D.
        /// </summary>
        /// <param name="vector">The Vector3D structure to be divided.</param>
        /// <param name="scalar">The scalar to divide vector by.</param>
        /// <returns>Returns the result of dividing vector by scalar.</returns>
        public static Vector3D Divide(Vector3D vector, double scalar)
        {
            return new Vector3D(vector.X / scalar, vector.Y / scalar, vector.Z / scalar);
        }

        /// <summary>
        /// Determines whether the specified Object is a Vector3D structure and whether the X, Y, and Z properties of the specified Object are equal to the X, Y, and Z properties of this Vector3D structure.
        /// </summary>
        /// <param name="o">The Object to compare.</param>
        /// <returns>True if o (the passed Object) is a Vector3D structure and is identical with this Vector3D structure; false otherwise.</returns>
        public override bool Equals(Object o)
        {
            if (o is Vector3D)
            {
                return Vector3D.Equals(this, (Vector3D)o);
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Compares two Vector3D structures for equality.
        /// </summary>
        /// <param name="value">The instance of Vector to compare against this instance.</param>
        /// <returns>true if instances are equal; otherwise, false.</returns>
        public bool Equals(Vector3D value)
        {
            return Vector3D.Equals(this, value);
        }

        /// <summary>
        /// Compares two Vector3D structures for equality.
        /// </summary>
        /// <param name="vector1">First Vector3D to compare.</param>
        /// <param name="vector2">Second Vector3D to compare.</param>
        /// <returns>True if the X, Y, and Z components of vector1 and vector2 are equal; false otherwise.</returns>
        public static bool Equals(Vector3D vector1, Vector3D vector2)
        {
            return vector1.X == vector2.X && vector1.Y == vector2.Y && vector1.Z == vector2.Z;
        }

        /// <summary>
        /// Gets a hash code for this Vector3D structure.
        /// </summary>
        /// <returns>Returns a hash code for this Vector3D structure.</returns>
        public override int GetHashCode()
        {
            return  (int)(this.X.GetHashCode() + this.Y.GetHashCode() + this.Z.GetHashCode());
        }

        /// <summary>
        /// Multiplies the specified scalar by the specified Vector3D structure and returns the result as a Vector3D.
        /// </summary>
        /// <param name="scalar">The scalar to multiply.</param>
        /// <param name="vector">The Vector3D structure to multiply.</param>
        /// <returns>Returns the result of multiplying scalar and vector.</returns>
        public static Vector3D Multiply(double scalar, Vector3D vector)
        {
            return new Vector3D(vector.X * scalar, vector.Y * scalar, vector.Z * scalar);
        }

        /// <summary>
        /// Multiplies the specified Vector3D structure by the specified scalar and returns the result as a Vector3D.
        /// </summary>
        /// <param name="vector">The Vector3D structure to multiply.</param>
        /// <param name="scalar">The scalar to multiply.</param>
        /// <returns>Returns the result of multiplying vector and scalar.</returns>
        public static Vector3D Multiply(Vector3D vector, double scalar)
        {
            return Vector3D.Multiply(scalar, vector);
        }

        /// <summary>
        /// Transforms the coordinate space of the specified Vector3D structure using the specified Matrix3D structure.
        /// </summary>
        /// <param name="vector">The Vector3D structure to transform.</param>
        /// <param name="matrix">The transformation to apply to the Vector3D structure.</param>
        /// <returns>Returns the result of transforming vector by matrix3D.</returns>
        public static Vector3D Multiply(Vector3D vector, Matrix3D matrix)
        {
            return new Vector3D();
        }

        /// <summary>
        /// Negates a Vector3D structure.
        /// </summary>
        /// <example>
        /// The following example shows how to negate a Vector3D structure.
        /// <code>
        /// // Negates a Vector3D using the Negate method.
        ///
        /// Vector3D vector1 = new Vector3D(20, 30, 40);
        /// Vector3D vectorResult = new Vector3D();
        ///
        /// vector1.Negate();
        /// // vector1 is equal to (-20, -30, -40)
        ///
        /// </code>
        /// </example>
        public void Negate()
        {
            this.mX = -1 * this.mX;
            this.mY = -1 * this.mY;
            this.mZ = -1 * this.mZ;
        }

        /// <summary>
        /// Normalizes the specified Vector3D structure.
        /// </summary>
        /// <remarks>
        /// A normalized Vector3D maintains its direction but its magnitude becomes 1. The resulting Vector3D is often called a unit vector. A Vector3D is normalized by dividing the Vector3D by its magnitude.
        /// </remarks>
        public void Normalize()
        {
            double l = this.Length;
            if (l != 0)
            {
                this.mX = this.mX / l;
                this.mY = this.mY / l;
                this.mZ = this.mZ / l;
            }
            else
            {
                this.mX = 0;
                this.mY = 0;
                this.mZ = 0;
            }
        }

        /// <summary>
        /// Converts a String representation of a 3-D vector into the equivalent Vector3D structure.
        /// </summary>
        /// <param name="source">The String representation of the 3-D vector.</param>
        /// <returns>Returns the equivalent Vector3D structure.</returns>
        public static Vector3D Parse(string source)
        {
            Point3D p = Point3D.Parse(source);
            return (Vector3D)p;
        }

        /// <summary>
        /// Subtracts a Point3D structure from a Vector3D structure.
        /// </summary>
        /// <param name="vector">The Vector3D structure to be subtracted from.</param>
        /// <param name="point">The Point3D structure to subtract from vector.</param>
        /// <returns>Returns the result of subtracting point from vector.</returns>
        public static Point3D Subtract(Vector3D vector, Point3D point)
        {
            return Point3D.Subtract(point, vector);
        }

        /// <summary>
        /// Subtracts a Vector3D structure from a Vector3D structure.
        /// </summary>
        /// <param name="vector1">The Vector3D structure to be subtracted from.</param>
        /// <param name="vector2">The Vector3D structure to subtract from vector1.</param>
        /// <returns>Returns the result of subtracting vector2 from vector1.</returns>
        public static Vector3D Subtract(Vector3D vector1, Vector3D vector2)
        {
            return new Vector3D(vector1.X - vector2.X, vector1.Y - vector2.Y, vector1.Z - vector2.Z);
        }

        /// <summary>
        /// Creates a String representation of this Vector3D structure.
        /// </summary>
        /// <returns>Returns a String containing the X, Y, and Z values of this Vector3D structure.</returns>
        public override string ToString()
        {
            return String.Format("{0}, {1}, {2}", this.mX, this.mY, this.mZ);
        }

        /// <summary>
        /// Creates a String representation of this Vector3D structure.
        /// </summary>
        /// <param name="provider">Culture-specific formatting information.</param>
        /// <returns>Returns a String containing the X, Y, and Z values of this Vector3D structure.</returns>
        public string ToString(IFormatProvider provider)
        {
            return String.Format(provider, "{0}, {1}, {2}", this.mX, this.mY, this.mZ);
        }

        #endregion

        #region IFormattable Members

        /// <summary>
        /// Creates a String representation of this Vector3D structure.
        /// </summary>
        /// <param name="format">Type: System.String</param>
        /// <param name="formatProvider">Type: System.IFormatProvider</param>
        /// <returns>String representation of this object.</returns>
        public string ToString(string format, IFormatProvider formatProvider)
        {
            return this.ToString(formatProvider);
        }

        #endregion
    }
}
